package com.yet.config;

import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.Environment;

import com.yet.container.loader.ConsumerServiceLoader;
import com.yet.container.loader.MultiServiceLoader;
import com.yet.container.registry.LocalServiceStateHolder;
import com.yet.container.registry.RegistryCenter;
import com.yet.container.registry.redis.JedisOperater;
import com.yet.container.registry.redis.RedisRegistry;
import com.yet.container.registry.zookeeper.CuratorProperties;
import com.yet.container.registry.zookeeper.ZookeeperRegistryCenter;
import com.yet.container.remote.Remoting;
import com.yet.container.remote.netty.NettyRemoting;
import com.yet.rule.Rule;
import com.yet.serialization.Serialization;

@Configuration
@ConditionalOnBean(RpcBeanDefinitionRegistrar.class)
@Import({ BeanConfiguration.RedisRegistryCenterConfiguration.class,
		BeanConfiguration.ZookeeperRegistryCenterConfiguration.class })
public class BeanConfiguration {

	public static final Logger logger = LoggerFactory.getLogger(BeanConfiguration.class);

	@Configuration
	@ConditionalOnProperty(name = "yet.registry.type", havingValue = "redis")
	static class RedisRegistryCenterConfiguration implements ApplicationContextAware, EnvironmentAware {

		private String name;

		private String rootPath;

		private ApplicationContext context;

		@Bean
		public RedisRegistry defaultRedisRegistry() {
			RedisRegistry redisRegistry = new RedisRegistry(name, rootPath);
			redisRegistry.setPublisher(context);
			return redisRegistry;
		}

		@Bean
		@ConfigurationProperties(prefix = "yet.redis", ignoreUnknownFields = true)
		public JedisOperater defaultJedisOperater(RedisRegistry redisRegistry) {
			JedisOperater jedisOperater = new JedisOperater();
			redisRegistry.setOperater(jedisOperater);
			return jedisOperater;
		}

		@Override
		public void setEnvironment(Environment environment) {
			this.name = environment.getProperty("yet.redis.name", "defaultRedis");
			this.rootPath = environment.getProperty("yet.redis.rootPath", "rpc-root");
		}

		@Override
		public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
			context = applicationContext;
		}

	}

	@Configuration
	@ConditionalOnProperty(name = "yet.registry.type", havingValue = "zookeeper")
	static class ZookeeperRegistryCenterConfiguration
			implements ApplicationContextAware, BeanFactoryAware, EnvironmentAware {

		private BeanFactory beanFactory;

		private ApplicationContext context;

		private String rootPath;

		private String name;

		@Bean
		@Order(3)
		public ZookeeperRegistryCenter defaultZookeeperRegistryCenter(Serialization serialization) {
			ZookeeperRegistryCenter zookeeperRegistryCenter = new ZookeeperRegistryCenter(rootPath, name);
			zookeeperRegistryCenter.setSerialization(serialization);
			zookeeperRegistryCenter.setPublisher(context);
			return zookeeperRegistryCenter;
		}

		@Bean
		@ConfigurationProperties(prefix = "yet.zookeeper", ignoreUnknownFields = true)
		public CuratorProperties defaultCuratorProperties(ZookeeperRegistryCenter zookeeperRegistryCenter) {
			CuratorProperties curatorProperties = new CuratorProperties();
			zookeeperRegistryCenter.setCuratorProperties(curatorProperties);
			zookeeperRegistryCenter.setBeanFactory(beanFactory);
			return curatorProperties;
		}

		@Override
		public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
			this.beanFactory = beanFactory;
		}

		@Override
		public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
			context = applicationContext;
		}

		@Override
		public void setEnvironment(Environment environment) {
			this.rootPath = environment.getProperty("yet.zookeeper.rootPath", "/rpc-root");
			this.name = environment.getProperty("yet.zookeeper.name", "defaultZookeeper");
		}

	}

	@Bean
	@ConditionalOnMissingBean(Rule.class)
	public Rule defaultRule() {
		return Rule.RANDOM;
	}

	@Bean
	@ConditionalOnMissingBean(ConsumerServiceLoader.class)
	public ConsumerServiceLoader consumerServiceLoader(RegistryCenter registryCenter, Rule rule) {
		return new ConsumerServiceLoader(registryCenter, rule);
	}

	@Bean
	@ConditionalOnBean(ConsumerServiceLoader.class)
	public MultiServiceLoader multiServiceLoader(Map<String, ConsumerServiceLoader> consumerServiceLoaders) {
		return new MultiServiceLoader(consumerServiceLoaders);
	}

	@Bean
	public LocalServiceStateHolder localServiceStateHolder(Map<String, RegistryCenter> registryCenters) {
		return new LocalServiceStateHolder(registryCenters);
	}

	@Bean
	@ConditionalOnMissingBean(Remoting.class)
	public Remoting nettyRemoting(Serialization serialization, MultiServiceLoader multiServiceLoader) {
		return new NettyRemoting(serialization, multiServiceLoader);
	}

}
